#!/usr/bin/env python3

from urllib.request import urlretrieve
import argparse
import json
import os
import re
import shutil, shlex
import subprocess
import sys
import tarfile
from pygit2 import Repository
import glob
from munch import Munch

import pathlib
for d in pathlib.Path(__file__).resolve().parents:
  if os.path.exists(os.path.join(d, 'behave.ini')):
    ROOT = d
    break
os.chdir(ROOT)
# because behave doesn't think it's useful to be able to load local stuff... oy...
sys.path.insert(0, os.path.abspath('test/features/steps'))

CI = Munch(
  service = (os.environ.get('TRAVIS_BRANCH') and 'travis') or (os.environ.get('GITHUB_SHA') and 'github'),
  branch = os.environ.get('TRAVIS_BRANCH') or (os.environ.get('GITHUB_REF', '').startswith('refs/heads/') and os.environ['GITHUB_REF'].split('/')[-1]) or '',
  event = (os.environ.get('TRAVIS_EVENT_TYPE') and ('schedule' if os.environ['TRAVIS_EVENT_TYPE'] == 'cron' else os.environ['TRAVIS_EVENT_TYPE'])) or os.environ.get('GITHUB_EVENT_NAME') or '',
  tag = os.environ.get('TRAVIS_TAG') or (os.environ.get('GITHUB_REF', '').startswith('refs/tags/') and os.environ['GITHUB_REF'].split('/')[-1]) or '',
  message = os.environ.get('TRAVIS_COMMIT_MESSAGE') or (os.environ.get('GITHUB_SHA') and subprocess.check_output(['git', 'log', '--format=%B', '-n', '1', os.environ['GITHUB_SHA']], encoding='UTF-8').strip()) or ''
)

def load_json(path, default):
  try:
    with open(path) as f:
      return json.load(f)
  except:
    return default
class BooleanAction(argparse.Action):
  def __init__(self, option_strings, dest, nargs=None, **kwargs):
    super().__init__(option_strings, dest, nargs=0, **kwargs)

  def __call__(self, parser, namespace, values, option_string=None):
    setattr(namespace, self.dest, not option_string.startswith('--no'))
class ClientAction(argparse.Action):
  def __init__(self, option_strings, dest, nargs=None, **kwargs):
    super().__init__(option_strings, dest, nargs=0, **kwargs)

  def __call__(self, parser, namespace, values, option_string=None):
    print(type(input_string), type(values))
    if option_string == '--jurism' and type(values) == bool and values:
      setattr(namespace, self.dest, 'jurism')
    elif option_string == '--jurism' and type(values) == bool and not values:
      setattr(namespace, self.dest, 'zotero')
    elif option_string == '--client' and type(values) == str and values in ['zotero', 'jurism']:
      setattr(namespace, self.dest, values)
    else:
      raise ValueError(f'Unexpected argument {option_string} = {values}')
parser = argparse.ArgumentParser()
parser.add_argument('--start', dest='start', action=BooleanAction)
parser.add_argument('--stop', '--no-stop', dest='stop', action=BooleanAction, default=(not CI.service))
parser.add_argument('--jurism', dest='client', action='store_const', const='jurism', default=os.environ.get('CLIENT', 'zotero'))
parser.add_argument('--client', dest='client', default=os.environ.get('CLIENT', 'zotero'))
parser.add_argument('--log-memory-every', dest='log_memory_every', type=int)
parser.add_argument('--beta', action='store_true', default=('#beta' in CI.message))
parser.add_argument('--keep', '--no-keep', dest='keep', action=BooleanAction, default=False)
parser.add_argument('--workers', '--no-workers', dest='workers', action=BooleanAction, default=True)
parser.add_argument('--caching', '--no-caching', dest='caching', action=BooleanAction, default=True)
parser.add_argument('--this', action='store_true')
parser.add_argument('--test-this', action='store_true')
parser.add_argument('--slow', action='store_true',
  default = CI.branch in load_json(os.path.join(ROOT, '.slow.json'), []) or
            CI.branch == 'master' or
            '#slow' in CI.message or
            CI.event == 'schedule' or
            CI.tag != ''
)
parser.add_argument('--test')
parser.add_argument('--bin')
parser.add_argument('--logs')
parser.add_argument('--prebuilt')
parser.add_argument('--tagged', action='store_true', default=CI.tag != '')
parser.add_argument('--nightly', action='store_true', default=(CI.event == 'schedule') or ('#nightly' in CI.message))
args, unknownargs = parser.parse_known_args()
sys.argv = sys.argv[:1]
if CI.branch != '' and args.logs:
  # prepend but replace later, because the final format is for the console
  sys.argv += [
    '--format', 'json.pretty',
    '--outfile', 'behave.json',
    '--define', 'loaded.json', # f"loaded={logfile('loaded')}",
  ]
sys.argv += unknownargs

if args.start:
  args.keep = True
  sys.argv += ['--tags', '@none']

if args.prebuilt:
  for xpi in glob.glob('xpi/zotero-better-bibtex*.xpi'):
    os.remove(xpi)
  xpi = glob.glob(f'prebuilt/zotero-better-bibtex*{args.prebuilt}*.xpi')[0]
  shutil.copy(xpi, 'xpi')
elif not CI.service : # local run
  process = subprocess.Popen(['npm', 'run', 'build'], stdout=subprocess.PIPE)
  while True:
    line = process.stdout.readline()
    print(line.decode('utf-8'), end='')
    if process.poll() is not None: break
  returncode = process.poll()
  if returncode != 0:
    print(f'Build exited with exit code {returncode}')
    sys.exit(returncode)

if args.nightly and CI.service and not args.tagged: # test for beta on nightly
  args.beta = (args.bin == '2')
elif CI.service and os.environ.get('BETA') == 'true':
  args.beta = True

if args.beta:
  args.bin = None

if args.this or args.test_this:
  repo = Repository('.')
  branch = repo.head.name.split('/')[-1]
  assert re.match(r'^gh-[0-9]+$', branch)
  if args.this: args.this = branch.replace('gh-', '@')
  if args.test_this: args.test = branch.replace('gh-', '')

if args.test or args.this or args.nightly or args.tagged or args.beta: args.slow = True

if args.client == 'jurism' and args.beta and CI.service:
  print(f"********* SKIPPING{' BETA' if args.beta else ''} BUILD FOR {args.client.upper()} UNTIL FURTHER NOTICE ****************")
  sys.exit()

sys.argv.extend(['--define', f"client={args.client}"])
sys.argv.extend(['--define', f'workers={str(args.workers).lower()}'])
sys.argv.extend(['--define', f'caching={str(args.workers).lower()}'])
if args.bin: sys.argv.extend(['--define', f"bin={args.bin}"])
sys.argv.extend(['--define', f'kill={str(not args.keep).lower()}'])
if args.stop: sys.argv.append('--stop')
if args.slow: sys.argv.extend(['--define', 'slow=true'])
if args.beta: sys.argv.extend(['--define', 'beta=true'])
if args.test: sys.argv.extend(['--define', f'test={args.test}'])
if args.this: sys.argv.extend(['--tags', args.this ])
if args.log_memory_every: sys.argv.extend(['--define', f'log_memory_every={args.log_memory_every}'])

if CI.branch != '' and args.logs:
  if not os.path.exists(args.logs): os.makedirs(args.logs)
  def replace_logfile(arg):
    if arg not in ['behave.json', 'loaded.json']: return arg
    name = os.path.splitext(arg)[0]
    if args.nightly:
      name = os.path.join(args.logs, f'{name}-{args.client}-{"beta" if args.beta else "release"}-{CI.branch}.json')
    else:
      name = os.path.join(args.logs, f'{name}-{args.client}-{args.bin}-{CI.branch}.json')
    if arg == 'behave.json':
      return name
    else:
      return f'loaded={name}'
  sys.argv = [replace_logfile(arg) for arg in sys.argv]

# *no* idea
def cleanup():
  if os.path.exists('travis.output'): os.remove('travis.output')
import atexit
atexit.register(cleanup)

print('prepped with', args)
print('starting with', ' '.join(sys.argv))

#https://stackoverflow.com/questions/28829350/run-python-behave-from-python-instead-of-command-line
from behave.__main__ import Configuration, run_behave
sys.exit(run_behave(Configuration(None)))
